Skip to content

perf(gemma4): regular decode steps stop rebuilding what did not change - #1000

Merged
FeathBow merged 14 commits into
pegainfer-project:mainfrom
FeathBow:perf/gemma4-decode-overhead
Aug 30, 2026
Merged

perf(gemma4): regular decode steps stop rebuilding what did not change#1000
FeathBow merged 14 commits into
pegainfer-project:mainfrom
FeathBow:perf/gemma4-decode-overhead

Conversation

@FeathBow

@FeathBow FeathBow commented Aug 30, 2026

Copy link
Copy Markdown
Collaborator

Description

Closes #999

The token read parked the caller on the stream every step and paid the OS scheduler's wake-up for it. It now spins on cuStreamQuery for a bounded window first, falling back to the parking wait so a stuck stream still sleeps.

Greedy decode rounds run a depth-two software pipeline on the one stream: the staged argmax writes the picks into the id buffer the next step's embedding reads, the readback lands in one of two pinned slots, and the emitted stream lags compute by one step. Any composition change, non-greedy row, logprob request or row within two tokens of its length cap drains to the collect-every-step path; a stop found late marks the row stopping and the drain retires it. The kernels and their order do not change, and the emitted tokens are the same bytes.

A regular step re-uploaded every per-step metadata table although their next values were already known. A kernel captured at the decode graph's tail now advances them in place, and a step whose fingerprint matches the previous one — every length one token further, page, chunk and split structure untouched — skips the rebuild and every upload. Mixed steps, chunk boundaries, page turns and the precapture warm pass invalidate the fingerprint and rebuild as before.

The staged sampler chain — suppression, argmax and the device copy of the picks — is captured per bucket at startup and launched as one graph beside the decode replay; only the pinned readback stays outside, so the collector keeps the copy's own event.

Test Env

Single GPU (sm_89, 48 GiB, x86_64)

Verification

  • cargo fmt --all -- --check and git diff --check against main pass. The Gemma 4 release server builds, all-target Gemma/kernels/sample Clippy passes with -D warnings, and release lib tests pass: core 33, Gemma 42 with 20 ignored, kernels 16 with 1 ignored, sample 3.

  • The engine and serving gates pass through scripts/gemma4_gates.sh, each running 1 test … 1 passed in its own process, the dual-checkpoint ones on both the 12B and the 26B: the shared and green lane lifecycles, the gathered lifecycle and the gathered walk against the serial path, the mixed step against the serial path, the overlapped prefill against the sync step, the ragged-batch row-order gate, and the 12B HF waypoint and greedy gates.

  • Byte identity at pinned composition: the same four greedy public /v1/completions requests (31-, 35- and 33-token prompts plus a 1421-token one; 64 forced tokens; finish_reason=length, completion_tokens=64) answer with one md5 on main's binary and on this head's, 219de1e3c09c.

  • Serving A/B, this head against main, vllm bench serve random 1024 in / 128 out at 8 / 24 / 64 prompts, two cooled alternating rounds, same seed per round, mean [range]:

    c1 c4 c16
    output tok/s, main 106.0 [105.2–106.7] 277.4 [275.6–279.1] 594.3 [590.7–597.8]
    output tok/s, this branch 107.8 [107.6–107.9] 282.5 [278.4–286.6] 600.6 [598.7–602.4]
    median TPOT ms, main 8.57 [8.57–8.57] 12.64 [12.60–12.68] 23.77 [23.71–23.83]
    median TPOT ms, this branch 8.45 [8.44–8.46] 12.26 [12.24–12.28] 23.33 [23.19–23.47]

    Median ITL follows TPOT (c1 8.51 → 8.38, c4 11.61 → 11.19, c16 16.31 → 16.07 ms). First-token time is not a claim of this change; one c4 round read a higher p99 on this branch (586.8 against 399.9 ms) that the other round did not (401.1 against 401.6 ms).

  • Node-level Nsight traces of the same request set (64 prompts at c16, then 8 at c1) on each binary: host-to-device copies 62,374 → 36,998 over the trace (the regular steps' metadata uploads gone; the bytes are the same 17.67 GB because the prefills' KV writes dominate), kernel launches 1,125,022 → 1,126,542 (one tail kernel and one sampler graph per step in place of the host-launched chain), inter-kernel gaps longer than 50 µs 1,695 → 641, summing 45.6 → 38.6 s over a trace span of 68.2 → 61.2 s with the GPU-busy total unchanged (22.49 → 22.51 s).

Signed-off-by: Feathbow <feathbow@gmail.com>
(cherry picked from commit 5c8c6c14165d03c90526a9e8886c25f07ae1581d)
Signed-off-by: Feathbow <feathbow@gmail.com>
Signed-off-by: Feathbow <feathbow@gmail.com>
Signed-off-by: Feathbow <feathbow@gmail.com>
Signed-off-by: Feathbow <feathbow@gmail.com>
…s them

Signed-off-by: Feathbow <feathbow@gmail.com>
Signed-off-by: Feathbow <feathbow@gmail.com>
…batch

Signed-off-by: Feathbow <feathbow@gmail.com>
@FeathBow
FeathBow marked this pull request as ready for review August 30, 2026 17:26

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 872c5d70a4

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread pegainfer-gemma4/src/engine.rs Outdated
Comment on lines +1017 to +1018
self.arena.invalidate_decode_fingerprint();
self.drain_pipeline(active);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid draining the decode pipeline when all slots are full

When concurrency exceeds self.slots, pending remains nonempty while active.len() == self.slots, so every engine-loop iteration reaches these calls even though the following admission loop cannot run. This invalidates the steady-decode metadata and drains the staged successor on every token, disabling both principal optimizations for the entire queued portion of a high-concurrency workload. Defer invalidation and draining until a slot is actually available and an admission can change the roster.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good Catch. Move the invalidation and clearing operations inside the loop, performing them only once before the first actual execution of admit_and_prefill.

}
}

const DECODE_PIPELINE_DEPTH: usize = 2;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Document the new Gemma decode pipeline contract

This introduces a depth-two speculative decode pipeline, delayed token collection, and new drain/invalidation requirements, but the commit changes no Gemma living documentation. Record the production invariant, why the row order must remain pinned, and the conditions that drain the pipeline in docs/models/gemma4/; otherwise future scheduler work has no durable description of the constraint it must preserve.

AGENTS.md reference: AGENTS.md:L176-L178

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

… admit

Signed-off-by: Feathbow <feathbow@gmail.com>
Signed-off-by: Feathbow <feathbow@gmail.com>
@FeathBow

Copy link
Copy Markdown
Collaborator Author

@codex check again please

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ca46460e6c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


## The decode pipeline

Greedy decode rounds run a depth-two software pipeline on the base stream: a step's argmax writes its picks straight into the id buffer the next step's embedding reads, its readback lands in one of two pinned slots, and the emitted token stream lags compute by one step. The production invariant is that token ids, finish reasons and token counts are identical to collecting every step at the same batch composition, and that the kernels inside a step and their order do not change.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add production evidence for the decode fast path

This section asserts that the new pipelined sampler and device-side metadata advancement preserve token IDs, finish reasons, and counts, but the commit adds neither a production E2E/model evaluation for those output invariants nor same-context before/after performance results. Because the new path is exercised only for eligible greedy batches and crosses roster, page/chunk, cancellation, EOS, and length transitions, provide production coverage of those cases plus an A/B benchmark before treating the optimization as verified.

AGENTS.md reference: AGENTS.md:L110-L112

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

Signed-off-by: Feathbow <feathbow@gmail.com>
Signed-off-by: Feathbow <feathbow@gmail.com>
…he roster

Signed-off-by: Feathbow <feathbow@gmail.com>
Signed-off-by: Feathbow <feathbow@gmail.com>
@FeathBow
FeathBow merged commit 6f0776e into pegainfer-project:main Aug 30, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gemma4: every decode step pays sampler, upload and metadata overhead it can skip

1 participant